/* readclass/functions/functions.go */
/* Written by, Sohail Qayum Malik[sqm@hackers.pk] */

package functions

import (

   "os"
   "log"
   "bytes"
   "reflect"
   "unsafe"
   "encoding/binary"
   "fmt"
)

func FromBigToLittleEndian(slice []byte) int32 {

   var num int32 = 0

   switch len(slice) {

      case 1:
         num = int32(slice[0])
      break
      case 2:
         num = int32(slice[0] << 8 | slice[1])
      break
      case 4:
         num = int32(slice[0] << 24 | slice[1] << 16 | slice[2] << 8 | slice[3])
      break
      case 8:
      break
      default:
      break
   }

   return num
}

func Getdata(fd *os.File, n int64) ([]byte, int64) {

   /* data, a slice */  
   data := make([]byte, n)
   count, err := fd.Read(data)
    
   if err != nil {
 
      log.Fatal(err)   
   }
   
   return data, int64(count) 
}

func GetReflectedSliceValue(data []byte, count int64, t *reflect.Type) {
}

func getReflectedValue(data []byte, count int64, t *reflect.Type) (reflect.Value, int64) {

   v := reflect.New(*t)
   e := v.Elem()
   
   var idx int64 = 0
   
   for i := 0; i < e.NumField(); i++ {
   
      tf := (*t).Field(i)
      tv := e.Field(i)
      
      switch ( tf.Type.Kind() ) {
      
         case reflect.Uint32:
         
            var s uint32 = 0
            
            binary.Read(bytes.NewBuffer(data[idx:idx+int64(unsafe.Sizeof(s))]), binary.BigEndian, &s)
            
            idx = idx + int64(unsafe.Sizeof(s))
            
            tv.SetUint(uint64(s)) 
         break
         case reflect.Uint16:
         
            var s uint16 = 0
            
            binary.Read(bytes.NewBuffer(data[idx:idx+int64(unsafe.Sizeof(s))]), binary.BigEndian, &s)
            
            idx = idx + int64(unsafe.Sizeof(s))
         
            tv.SetUint(uint64(s))
         break
         default:
	    fmt.Println("default reached in function, GetReflectedData()")
         break 
      }
   }   
 
   return v, idx
}

func GetClassFile(data []byte, count int64) {
  
   t := reflect.TypeOf(ClassFileBeforeCpInfo{})
   v, idx := getReflectedValue(data, count, &t)
   /* struct ClassFile{} before, struct cp_info{} gets collected here */
   cfo_before_CpInfo := *v.Interface().(*ClassFileBeforeCpInfo)

   //var foo uint16 

   fmt.Println("type ClassFileBeforeCpInfo struct {}");
   fmt.Println("---------------------------------------");
   fmt.Printf("Magic 0x%x\n", cfo_before_CpInfo.Magic)
   fmt.Printf("Minor 0x%x(%d)\n", cfo_before_CpInfo.MinorVersion, cfo_before_CpInfo.MinorVersion)
   fmt.Printf("Major 0x%x(%d)\n", cfo_before_CpInfo.MajorVersion, cfo_before_CpInfo.MajorVersion)
   fmt.Printf("Pooln 0x%x(%d)(Number of entries in constant pool table and then plus one)\n", cfo_before_CpInfo.ConstantPoolCount, cfo_before_CpInfo.ConstantPoolCount)
   fmt.Println("----------------------------------------------------------------------------")

   fmt.Println("Constant Pool Table");
   fmt.Println("----------------------");
   fmt.Println("The constant pool table is a set of structures. Each structure represents a string constant, class or interface name, a field name, and any other constant which is referred within the ClassFile structure and its substructures. The format of each constant pool table entry is indicated by its first \"tag\" byte");
   fmt.Println("-------------------------------------------------------------------------------")
   fmt.Println("Please Note:- One additional byte = Tag of next CpInfo entry");
   fmt.Println("-------------------------------------------------------------------------------");

   var Info []byte /* This is our ClassFile::constant_pool */  

/*
   fmt.Println("-------------------------")
   fmt.Println(data[idx])
   fmt.Println(data[idx + 1])
   fmt.Println(data[idx + 2])
   fmt.Println(data[idx + 3])
   fmt.Println(data[idx + 4])
   fmt.Println(data[idx + 5])
   fmt.Println(data[idx + 6])
   fmt.Println(data[idx + 7])
   fmt.Println(data[idx + 8])
   fmt.Println(data[idx + 9])
   fmt.Println(data[idx + 10])
   fmt.Println(data[idx + 11])

   fmt.Println("-------------------------")
*/   
   
   data = data[idx:]
   count = count - idx;
   idx = 0;

   /* Finally our ClassFile::cp_info constant_pool[constant_pool_count - 1] is range Info[] */
   for i := 0; i < int(cfo_before_CpInfo.ConstantPoolCount) - 1; i++ {

      fmt.Printf("%d) ", i + 1);

      switch data[idx] {
        
	 case Constant_Utf8:
	    //var l uint16 = uint16(FromBigToLittleEndian(data[idx + 1:idx + 3])) /* uint16(((data[idx + 1] << 8) | data[idx + 2])) */
	    info := make([]byte, int32(Sizeof_Tag) + /* int64(l) */ FromBigToLittleEndian(data[idx+1:idx+3]) + int32(Sizeof_ConstantUtf8Info))
	    temp_Info := make([]byte, len(Info) + len(info))
	    for i := 0; i < len(info); i++ {

	       info[i] = data[idx + int64(i)]
	    }
	    for i := 0; i < len(Info); i++ {

	       temp_Info[i] = Info[i]
	    }
	    for i := len(Info); i < len(Info) + len(info); i++ {

	       temp_Info[i] = info[ i - len(Info)]
	    }
	    Info = temp_Info
	    fmt.Printf("Constant_Utf8 %d, Length = %d + one additional byte = %d\n", /* data[idx] */ /* info[0] */ Info[len(Info) - len(info)], /* l*/ FromBigToLittleEndian(Info[len(Info) - len(info) + 1:len(Info) - len(info) + 3]) /*FromBigToLittleEndian(info[1:3])*/ , /*data[idx + 2 + int64(l) + int64(Sizeof_Tag)]*/ data[idx + int64(len(info))])
	    for i := 0; i < int(FromBigToLittleEndian(Info[len(Info) - len(info)+ 1 : len(Info) - len(info) + 3]))/* int(FromBigToLittleEndian(info[1:3]))*/; i++ {

	       fmt.Printf("%c", Info[len(Info) - len(info) + 3 + i]/*info[3 + i]*/)
	    }
	    fmt.Printf("\n")
	    idx = idx + int64(len(info)) /* int64(Sizeof_Tag) + 2 + int64(l); */
	 break
	 case Constant_Integer:
	    fmt.Println("Constant_Integer")
	 break
	 case Constant_Float:
	    fmt.Println("Constant_Float")
	 break
	 case Constant_Long:
	    fmt.Println("Constant_Long")
	 break
	 case Constant_Double:
	    fmt.Println("Constant_Long")
	 break
	 case Constant_Class:
	    info := make([]byte, Sizeof_Tag + Sizeof_ConstantClassInfo)
	    temp_Info := make([]byte, len(Info) + len(info))
	    for i := 0; i < len(info); i++ {

	       info[i] = data[idx + int64(i)]
	    }
            for i := 0; i < len(Info); i++ {

	       temp_Info[i] = Info[i]
	    }
            for i := len(Info); i < len(temp_Info); i++ {

	       temp_Info[i] = info[i - len(Info)]
	    }
	    Info = temp_Info
	    fmt.Printf("Constant_Class %d, The next 2 bytes are %d, %d(An index into Constant Pool Table, originating at 1) + one additional byte = %d\n", Info[len(Info) - len(info)]  /* info[0] */ /* data[idx] */, Info[len(Info) - len(info) + 1] /* info[1] */ /* data[idx + 1] */, Info[len(Info) - len(info) + 2]/* info[2] */ /* data[idx + 2] */, data[idx + 3])
	    idx = idx + int64(len(info))
	    /*idx = idx + int64(Sizeof_Tag)
	    idx = idx + int64(Sizeof_ConstantClassInfo)*/
	 break
	 case Constant_String:
	    /*fmt.Println("7")
            info := make([]byte, Sizeof_Tag + Sizeof_ConstantStringInfo)
            info[0] = data[idx] */
            idx = idx + int64(Sizeof_Tag) 
	    /*
            for i := 0; i < int(Sizeof_ConstantStringInfo); i++ {
               info[i + 1] = data[int64(i) + idx]
	    }
	    */
	    /* temp_info := make([]byte, len(Info) + len(info))
	    for i := 0; i < len(Info); i++ {

	       temp_info[i] = Info[i]
	    }
	    for i := 0; i < len(info); i++ {

	       temp_info[len(Info) + i] = info[i] 
	    }
	    Inf o = temp_info */
	    idx = idx + int64(Sizeof_ConstantStringInfo)
	 break
	 case Constant_Fieldref:
	    fmt.Println("Constant_Fieldref")
	 break
	 case Constant_Methodref:
            info := make([]byte, Sizeof_Tag + Sizeof_ConstantMethodRefInfo)
	    temp_Info := make([]byte, len(Info) + len(info))
	    for i := 0; i < len(info); i++ {

	       info[i] = data[idx + int64(i)]
	    }
	    for i := 0; i < len(Info); i++ {

	       temp_Info[i] = Info[i]
	    }
	    for i := len(Info); i < len(temp_Info); i++ {

	       temp_Info[i] = info[i - len(Info)]
	    }
	    Info = temp_Info
	    fmt.Printf("Constant_Methodref %d The next four bytes = %d, %d, %d, %d + one additional byte = %d\n", /*data[idx]*/ /*info[0]*/ Info[len(Info) - len(info)], /*data[idx + 1]*/ /* info[1]*/ Info[len(Info) - len(info) + 1], /*data[idx + 2]*/ /* info[2] */ Info[len(Info) - len(info) + 2], /* data[idx + 3]*/ /*info[3]*/ Info[len(Info) - len(info) + 3], /*data[idx + 4]*/ /*info[4]*/ Info[len(Info) - len(info) + 4], data[idx + 5])
            /*info := make([]byte, Sizeof_Tag + Sizeof_ConstantMethodRefInfo)
	    info[0] = data[idx]*/
	    /*idx = idx + int64(Sizeof_Tag)*/
	   /* for i := 0; i < int(Sizeof_ConstantMethodRefInfo); i++ {
               info[i + 1] = data[idx + int64(i)]
	    }
	    temp_Info := make([]byte, len(Info) + len(info))
	    for i := 0; i < len(Info); i++ {

	       temp_Info[i] = Info[i]
	    }
	    for i := 0; i < len(info); i++ {

	       temp_Info[len(Info) + i] = info[i]
	    }
	    Info = temp_Info*/
	    //fmt.Println(Sizeof_ConstantMethodRefInfo)
	    //fmt.Println("--------------------------------------")
	    idx = idx + /*int64(Sizeof_ConstantMethodRefInfo)*/ int64(len(info))
	 break
	 case Constant_InterfaceMethodref:
	    fmt.Println("10") 
         break
	 case Constant_NameAndType:
	    info := make([]byte, Sizeof_Tag + Sizeof_ConstantNameAndTypeInfo)
	    temp_Info := make([]byte, len(Info) + len(info))
	    for i := 0; i < len(info); i++ {

	       info[i] = data[idx + int64(i)]
	    }
	    for i := 0; i < len(Info); i++ {

	       temp_Info[i] = Info[i]
	    }
	    for i := len(Info); i < len(temp_Info); i++ {

	       temp_Info[i] = info[i - len(Info)]
	    }
	    Info = temp_Info
	    fmt.Printf("Constant_NameAndType %d, The next four bytes are %d, %d, %d, %d + one additional byte = %d\n", /* data[idx] */ /* info[0]*/ Info[len(Info) - len(info)], /* data[idx + 1] */ /* info[1]*/ Info[len(Info) - len(info) + 1], /*data[idx + 2]*/ /*info[2]*/ Info[len(Info) - len(info) + 2], /*data[idx + 3]*/ /*info[3]*/ Info[len(Info) - len(info) + 3], /*data[idx + 4]*/ /*info[4]*/ Info[len(Info) - len(info) + 4], data[idx + 5]) 
            idx = idx + /* int64(Sizeof_Tag) + int64(Sizeof_ConstantNameAndTypeInfo)*/ + int64(len(info)) 
	 break
	 default:
	    //fmt.Println(data[idx])
	    log.Fatal("Unknown Tag")
	    //idx = idx + 1
	 break
      }

      //CpInfo Object 
      //var cpio CpInfo
   }
   
   fmt.Println("-------------------------------------------------------------------------------");

   data = data[idx:]
   count = count - idx;

   /* Read after Constant Poll Table and before Interfaces Table */
   t = reflect.TypeOf(ClassFileAfterCpInfo{})
   v, idx = getReflectedValue(data, count, &t)
   cfo_after_CpInfo := *v.Interface().(*ClassFileAfterCpInfo)

   data = data[idx:]
   count = count - idx;
   idx = 0

   /* Each value in the interfaces array must be a valid index into the constant _pool table. The constant_pool entry at each value of of interfaces[i], where i >= 0 and i < interfaces_count, must be a Constant_Class_info structure representing an interface that is direct superinterface of this class or interface type  */
   interfaces := make([]uint16, cfo_after_CpInfo.InterfaceCount) /* This is where interfaces related data gets stored */

   //fmt.Println(idx);

   //fmt.Println(FromBigToLittleEndian(cfo_after_CpInfo.InterfaceCount))

   for i := 0; i < int(cfo_after_CpInfo.InterfaceCount); i++ {

      //temp_uint16 := data[idx]
      temp_uint16 := data[idx] | data[idx + 1] << 8 /* We store it in big endian format */
      interfaces[i] = uint16(temp_uint16) /* uint16(i) */ /* uint16(data[idx:idx+2]) */
      idx = idx + 2  

      //fmt.Println(i)
   }

   //fmt.Printf("Sizeof uint = %d", unsafe.Sizeof(foo));

   data = data[idx:]
   count = count - idx
   idx = 0

   
   fmt.Printf("AccessFlags = %d\n",cfo_after_CpInfo.AccessFlags);
   fmt.Printf("ThisClass = %d(An index into Constant Pool Table, originating at 1)\n", cfo_after_CpInfo.ThisClass);
   fmt.Printf("SuperClass = %d(An index into Constant Pool Table, originating at 1)\n", cfo_after_CpInfo.SuperClass);

   /* Interfaces */
   fmt.Printf("Number of Interfaces = %d\n", int(cfo_after_CpInfo.InterfaceCount));

   t = reflect.TypeOf(ClassFileAfterCpInfoAndInterfaces{})
   v, idx = getReflectedValue(data, count, &t)
   cfo_after_CpInfoAndInterfaces := *v.Interface().(*ClassFileAfterCpInfoAndInterfaces)

    fmt.Printf("Number of Fields = %d", cfo_after_CpInfoAndInterfaces.FieldsCount);


   //var foo CpInfo 

   //fmt.Println(unsafe.Sizeof(foo))
}
